package com.codemonkey.lq.qd.builder.xml;


import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;

import com.codemonkey.lq.qd.ServiceContext;
import com.codemonkey.lq.qd.builder.BaseBuilder;
import com.codemonkey.lq.qd.builder.BuilderPostProcessor;
import com.codemonkey.lq.qd.facotry.config.AbstractActionDefinition;
import com.codemonkey.lq.qd.facotry.config.AbstractFlowDefinition;
import com.codemonkey.lq.qd.facotry.config.ActionDefinition;
import com.codemonkey.lq.qd.facotry.config.FlowDefinition;
import com.codemonkey.lq.qd.facotry.config.SConfiguration;
import com.codemonkey.lq.qd.facotry.config.ServiceDefinition;
import com.codemonkey.lq.qd.facotry.support.GenericFlowDefinition;
import com.codemonkey.lq.qd.facotry.support.LinkedActionDefinition;
import com.codemonkey.lq.qd.utils.StringHelper;

/**
 * 专门用于处理XMLServiceBuilder
 * @author LQ
 *
 */
public final class XMLServiceBuilderPostProcessor implements BuilderPostProcessor {

	/**
	 * ServiceDefinition中存储action 集合的那个字段的名字.我们使用它来通过反射(Mybatis简化了这步操作)获取该容器.
	 */
	private static final String NAME_ACTION_CONTAINER = "actionContainer";
	/**
	 * ServiceDefinition中存储flow Collection的那个字段的名字.
	 */
	private static final String NAME_FLOWS = "flows";

	private static final ActionDefinition EMPTY_AD = new AbstractActionDefinition() {
		@Override
		public void execute(ServiceContext serviceContext) {
			//DO NOTHING
		}
	};

	@SuppressWarnings("unchecked")
	@Override
	public void postProcessAfterBuild(BaseBuilder builder, Object obj, SConfiguration configuration) {
		if (builder instanceof XMLServiceBuilder) {
			ServiceDefinition sd = (ServiceDefinition) obj;

			// 使用MyBatis已有功能来简化反射的使用
			MetaObject sdMetaObject = SystemMetaObject.forObject(sd);
			Collection<FlowDefinition> flowGroup = (Collection<FlowDefinition>) sdMetaObject
					.getValue(NAME_FLOWS);
			Map<String, ActionDefinition> actionContainer = ((Map<String, ActionDefinition>) sdMetaObject
					.getValue(NAME_ACTION_CONTAINER));

			if (flowGroup.size() == 0) { 
				// 如果没有定义flow-list,就直接顺序执行.
				// 本service中没有定义flow-list,不再给它构建一个.
				return;
				//buildFlowList(flowGroup, actionContainer.values());
			}

			refactServiceDefinition(actionContainer, sd);
		}
	}

	private void buildFlowList(Collection<FlowDefinition> flowGroup,
			Collection<ActionDefinition> actionGroup) {
		ActionDefinition[] arrayAD = actionGroup.toArray(new ActionDefinition[actionGroup.size()]);

		ActionDefinition previousAD = null;
		for (int i = 0; i < arrayAD.length; i++) {
			previousAD = arrayAD[i];

			ActionDefinition currentAD = arrayAD[i];

			AbstractFlowDefinition fd = new GenericFlowDefinition();
			if (0 == i) { //第一个节点
				fd.setFrom(FlowDefinition.START);
				fd.setTo(currentAD.getName());
				fd.setCondition(currentAD.getCondition());
			} else {
				fd.setFrom(previousAD.getName());
				fd.setTo(currentAD.getName());
				fd.setCondition(currentAD.getCondition());
			}

			flowGroup.add(fd);

			if (i == arrayAD.length - 1) { //最后一个节点
				AbstractFlowDefinition fd_last = new GenericFlowDefinition();
				fd.setFrom(currentAD.getName());
				fd.setTo(FlowDefinition.END);
				fd.setCondition(currentAD.getCondition());

				flowGroup.add(fd_last);
			}
		}
	}

	// 因为使用的是action之间互相链接
	// 所以我们将这个集合中的元素清空后,只加入起点元素.
	// 然后在这里组织好之后,会从起点元素一步步往前推进的.
	// 我们只负责组织好各个action之间的关联关系;下一个action都有哪些,以及执行下一个action的条件是什么
	// 这源码没白看！！！
	private void refactServiceDefinition(Map<String, ActionDefinition> actionContainer_orgin,
			ServiceDefinition sd) {
		LinkedActionDefinition startAD = LinkedActionDefinition.of_START_AD(EMPTY_AD, sd);
		// 因为使用的是action之间互相链接
		// 所以我们将这个集合中的元素清空后,只加入起点元素.
		// 之后的元素之间只是彼此关联

		Collection<FlowDefinition> flowGroup = sd.getFlowGroup();

		recursionAddAD(flowGroup, sd, startAD, new ArrayList<LinkedActionDefinition>(
				actionContainer_orgin.size()));

		actionContainer_orgin.clear();
		sd.addAction(startAD);
	}

	private void recursionAddAD(Collection<FlowDefinition> flowGroup, ServiceDefinition sd,
			LinkedActionDefinition parentAD, List<LinkedActionDefinition> ladGroup_hasGenerated) {
		FlowDefinition[] fdArray = flowGroup.toArray(new FlowDefinition[flowGroup.size()]);
		for (int i = 0; i < fdArray.length; i++) {
			FlowDefinition fd = fdArray[i];
			if (fd.getFrom().equalsIgnoreCase(parentAD.getName())) {

				String condition = fd.getCondition();
				//如果本flow没有先决条件,即直接通过
				condition = StringHelper.isEmpty(condition) ? FlowDefinition.NO_CONDITION
						: condition;

				String to = fd.getTo();
				if (isEndPoint(to)) {
					parentAD.addNextAD(condition, LinkedActionDefinition.of_END_AD(EMPTY_AD, sd,parentAD));
					break;
				}
				ActionDefinition specialAction = sd.getSpecialAction(to);
				if (null == specialAction) {
					String errorInfo = String
							.format(
									"in the current service context of [%s],can not found the action which named : '%s'",
									sd.getId(), to);
					throw new IllegalArgumentException(errorInfo);
				}

				LinkedActionDefinition lad = null;
				for (LinkedActionDefinition ladItem : ladGroup_hasGenerated) {
					if (ladItem.getName().equalsIgnoreCase(specialAction.getName())) {
						lad = ladItem;
						break;
					}
				}

				if (null == lad) {
					lad = parentAD.addNextAD(condition, specialAction);
					// 留作备份,如果之前生成过,直接取出来.不再从新生成.
					// 感谢SICP!!!
					ladGroup_hasGenerated.add(lad);
				}

				//递归调用
				recursionAddAD(flowGroup, sd, lad, ladGroup_hasGenerated);
			}
		}
	}

	private boolean isEndPoint(String pointName) {
		return pointName.equalsIgnoreCase(FlowDefinition.END);
	}

	@Override
	public void postProcessBeforeBuild(BaseBuilder builder, SConfiguration configuration) {
		//DO NOTHING
	}
}
